#include <QCustomPlot/qcustomplot.h>
#include "mywidget.h"
#include "mytablemodel.h"
#include <eigenhelper.h>
#include "splinefitter.h"
#include "olsfitter.h"

//署名
QVector<QVector<double>> easterEgg() {

    return QVector<QVector<double>>{QVector<double>{
            {105, 60, 63, 66, 69, 1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34, 37, 57, 70,
                    105, 55, 53, 43, 71, 105, 20, 44, 52, 69, 80, 20, 79, 88, 92, 96, 100, 105,
                    110, 114, 118, 122, 45, 51, 64, 78, 63, 64, 20, 46, 62, 63, 64, 76, 105, 51,
                    62, 63, 74, 46, 61, 62, 95, 20, 60, 61, 105, 112, 115, 118, 95, 5, 8, 11, 14,
                    17, 20, 23, 26, 29, 32, 34, 110, 120, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73,
                    76, 95, 109, 20, 100, 122, 102, 61, 95, 104, 109, 20, 87, 106, 61, 89, 95, 109,
                    20, 111, 91, 113, 43, 46, 49, 52, 55, 58, 61, 64, 67, 70, 73, 76, 79, 95, 109,
                    116, 20, 93, 118, 61, 95, 109, 120, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33,
                    36, 96, 61, 109, 98, 61}
    }, QVector<double>{0, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 5, 6, 6, 6, 7,
                       8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12,
                       13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17,
                       17, 17, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 21, 21, 21, 21,
                       21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 23, 24, 24, 24, 24, 25, 25, 25,
                       27, 27, 27, 27, 28, 28, 29, 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
                       30, 30, 30, 30, 31, 31, 31, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34,
                       34, 34, 34, 35, 36, 36, 37, 38}};
}

void MyWidget::initControlPanel() {
    m_fitPointsCount = 400;
    m_slider = new QSlider(Qt::Horizontal);
    m_slider->setRange(10, 200);
    m_slider->setValue(static_cast<int>(m_fitPointsCount) / 10);
    m_slider->setTracking(false);
    m_labPointsCount = new QLabel(QString(u8"拟合点数:%1").arg(m_fitPointsCount));
    m_labPerimeter = new QLabel(u8"拟合曲线总长度: 0");
    m_labEval = new QLabel("预估值：");

    m_comboDoundary = new QComboBox;
    m_btnOpen = new QPushButton(u8"打开CSV文件");
    m_btnOpen->setMinimumHeight(40);
    m_btnSaveFigure = new QPushButton("导出图像");
    m_btnSaveFigure->setMinimumHeight(40);

    m_spinLeft = new QDoubleSpinBox;
    m_spinLeft->setMinimum(-100.0);
    m_spinLeft->setMaximum(100.0);
    m_spinLeft->setValue(0.0);
    m_spinLeft->setEnabled(false);
    m_spinRight = new QDoubleSpinBox;
    m_spinRight->setMinimum(-100.0);
    m_spinRight->setMaximum(100.0);
    m_spinLeft->setValue(0.0);
    m_spinRight->setEnabled(false);
    m_spinEval = new QDoubleSpinBox;
    m_spinEval->setEnabled(false);
    m_spinRight->setEnabled(false);
    m_layoutControl = new QGridLayout;
    m_layoutControl->addWidget(m_labPointsCount, 0, 0);
    m_layoutControl->addWidget(m_slider, 0, 1);
//    m_layoutControl->addWidget(new QLabel(u8"边界条件"),1,0);
    m_layoutControl->addWidget(m_comboDoundary, 1, 1);
//    m_layoutControl->addWidget(new QLabel(u8"最左边一阶导"),2,0);
//    m_layoutControl->addWidget(new QLabel(u8"最右边一阶导"),3,0);
    m_layoutControl->addWidget(m_spinLeft, 2, 1);
    m_layoutControl->addWidget(m_spinRight, 3, 1);
    m_layoutControl->addWidget(m_labEval, 4, 0);
    m_layoutControl->addWidget(m_spinEval, 4, 1);
    m_layoutControl->addWidget(m_labPerimeter, 5, 0);

    auto layout = new QHBoxLayout;
    layout->addWidget(m_btnOpen);
    layout->addWidget(m_btnSaveFigure);
    m_layoutControl->addLayout(layout, 5, 1);

    m_layoutControl->setColumnStretch(0, 1);
    m_layoutControl->setColumnStretch(1, 1);
    m_layoutControl->setContentsMargins(10, 0, 0, 0);

}

void MyWidget::initPlotCanvas(int i) {
    m_plot = new QCustomPlot;

    //第一题把图拟合点放上面，第二题放下面好看一点
    if (i == 1) {
        m_curve = m_plot->addGraph();
        m_scatter = m_plot->addGraph();
    } else {
        m_scatter = m_plot->addGraph();
        m_curve = m_plot->addGraph();
    }
    m_scatter->setLineStyle(QCPGraph::lsNone);
    QCPScatterStyle myScatter;
    myScatter.setShape(QCPScatterStyle::ssCircle);
    myScatter.setPen(QPen(QColor("#77ac30")));
    myScatter.setBrush(QBrush(QColor("#4cd137")));
    myScatter.setSize(8);

    if (i == 1) {
        myScatter.setSize(8);
    } else {
        myScatter.setSize(2);
    }
    m_scatter->setScatterStyle(myScatter);
    m_scatter->setName("原始数据");

    m_curve->setName("拟合曲线");
    m_curve->setPen(QPen(QColor("#4dbeee"), 2));
    m_curve->setAntialiased(true);
    m_curve->setAdaptiveSampling(false);

    m_plot->setMinimumSize(640, 480);
    m_plot->legend->setVisible(true);
    m_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); //|QCP::iSelectLegend );

    auto mylogo = easterEgg();
    m_scatter->setData(mylogo.at(0), mylogo.at(1));
    m_plot->xAxis->setRange(0, 128);
    m_plot->yAxis->setRange(-30, 60);

}

void MyWidget::initTableView() {
    m_tableView = new QTableView;
    m_model1 = new MyTableModel(this);
    m_tableView->setModel(m_model1);
    m_tableView->setMinimumWidth(300);
    m_tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
    m_tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
}


void MyWidget::connectSingals() {


    connect(m_slider, &QSlider::valueChanged, [this]() {
        setFitPointsCount(m_slider->value() * 10);
    });

    connect(m_model1, &QAbstractTableModel::modelReset, this, &MyWidget::reset);
    connect(m_model1, &QAbstractTableModel::dataChanged, this, &MyWidget::reset);

    connect(m_btnSaveFigure, &QPushButton::clicked, [this]() {
        auto str = QFileDialog::getSaveFileName(this, "保存图像",
                                                QStandardPaths::writableLocation(
                                                        QStandardPaths::PicturesLocation).append("/第一题.png"),
                                                "PNG (*.png);;JPG (*.jpg);;PDF (*.pdf)");
        if (str.isEmpty()) {
            return;
        }
        if (str.endsWith(".jpg", Qt::CaseInsensitive)) {
            m_plot->saveJpg(str, 0, 0, 1.0, -1, 300);
        } else if (str.endsWith(".png", Qt::CaseInsensitive)) {
            m_plot->savePng(str, 0, 0, 1.0, -1, 300);
        } else if (str.endsWith(".pdf", Qt::CaseInsensitive)) {
            m_plot->savePdf(str);
        } else {
            str.append(".pdf");
            m_plot->savePdf(str);
        }
    });

    connect(m_spinEval, qOverload<double>(&QDoubleSpinBox::valueChanged), [this](double value) {
        if (m_model1->rowCount() == 0) {
            return;
        }

        m_labEval->setText(QString("预估值：%1").arg(QString::number((*m_abstractFitter)(value), 'f', 2)));
    });

    connect(m_model1, &MyTableModel::headerDataChanged, [this]() {
        m_plot->xAxis->setLabel(m_model1->headerData(0, Qt::Horizontal, Qt::DisplayRole).toString());
        m_plot->yAxis->setLabel(m_model1->headerData(1, Qt::Horizontal).toString());
        m_plot->replot();
    });

}


void MyWidget::setParamater() {

}

QPointF MyWidget::fitRange() {
    return {0.0, 0.0};
}

MyWidget::MyWidget(QWidget *parent)
        : QWidget(parent) {

}


MyTableModel *MyWidget::model() {
    return m_model1;
}

void MyWidget::refit() {
    if (m_model1->rowCount() == 0) {
        return;
    }
    auto xx_range = fitRange();
    Eigen::VectorXd xx = Eigen::VectorXd::LinSpaced(
            m_fitPointsCount, xx_range.x(), xx_range.y());
    auto yy = m_abstractFitter[0](xx);
    m_curve->setData(toQVector(xx), toQVector(yy));
    m_plot->rescaleAxes();
    m_plot->replot();
}

void MyWidget::setFitPointsCount(size_t n) {
    m_fitPointsCount = n;
    m_labPointsCount->setText(QString(u8"拟合点数：%1").arg(n));
    refit();
}

void MyWidget::reset() {
    if (m_model1->rowCount() == 0) {
        return;
    }
    auto x_qvec = m_model1->getX();
    auto y_qvec = m_model1->getY();
    auto x = Vecd::Map(x_qvec.constData(), x_qvec.size());
    auto y = Vecd::Map(y_qvec.constData(), y_qvec.size());
    setParamater();
    m_abstractFitter->setPoints(x, y);
    m_scatter->setData(x_qvec, y_qvec);
    refit();
}








